Consignas
A partir del corpus deberán: * Cargar los datos * Preprocesarlos (normalizar texto, eliminar stopwords) * Generar una matriz token-por-fila para cada documento
A continuación deberán responder las siguientes preguntas:
- ¿Cuáles son las palabras más utilizadas en cada uno de los medios? ¿Pueden verse diferencias? (Tener en cuenta las diferentes métricas trabajadas en el curso: tf, tf-idf, etc.)
- Generar las visualizaciones que considere más pertinentes para responder la pregunta
- ¿Cuáles son los tópicos principales en el corpus? ¿Pueden evidenciar diferencias en cada uno de los medios? ¿Cómo evolucionan los tópicos en el tiempo?
- Explicar qué método se utilizó para responder la pregunta, cuáles son los supuestos del mismo.
- Generar las visualizaciones más adecuadas para responder a las preguntas
Librerías
library(tidyverse) # Easily Install and Load the 'Tidyverse'
library(tidytext) # Text Mining using 'dplyr', 'ggplot2', and Other Tidy Tools
library(igraph) # Network Analysis and Visualization
library(ggraph) # An Implementation of Grammar of Graphics for Graphs and Networks
library(grid) # The Grid Graphics Package
library(topicmodels) # Topic Models
library(knitr) # A General-Purpose Package for Dynamic Report Generation in R
library(gganimate) # A Grammar of Animated GraphicsDatasets
Corpus de noticias “scrapeadas” entre julio y septiembre de 2019
raw_data <- read_csv("data/M5_corpus_medios.csv")Exploramos rápidamente el dataset de noticias
# chequeo variables del dataset
names(raw_data)## [1] "id" "url" "fecha" "anio" "mes" "dia" "medio" "titulo"
## [9] "texto"
# vistazo rápido del dataset
raw_data %>%
head(10)## # A tibble: 10 × 9
## id url fecha anio mes dia medio titulo texto
## <dbl> <chr> <date> <dbl> <dbl> <dbl> <chr> <chr> <chr>
## 1 51960 https://www.clarin.com… 2019-07-01 2019 7 1 clar… "Copa… "Arg…
## 2 52024 https://www.clarin.com… 2019-07-01 2019 7 1 clar… "Robo… "La …
## 3 52023 https://www.clarin.com… 2019-07-01 2019 7 1 clar… "Inve… "La …
## 4 52095 http://luz.perfil.com/… 2019-07-01 2019 7 1 perf… "Arti… "Art…
## 5 51863 https://www.perfil.com… 2019-07-01 2019 7 1 perf… "Cier… "mar…
## 6 52087 https://exitoina.perfi… 2019-07-01 2019 7 1 perf… "Tele… "Con…
## 7 52084 https://www.clarin.com… 2019-07-01 2019 7 1 clar… "Vene… "Car…
## 8 51945 https://www.minutouno.… 2019-07-01 2019 7 1 minu… "La a… "El …
## 9 51928 https://www.clarin.com… 2019-07-01 2019 7 1 clar… "¿Mit… "Alg…
## 10 52375 https://www.clarin.com… 2019-07-01 2019 7 1 clar… "Los … "Hay…
# chequeo resumen del dataset
summary(raw_data)## id url fecha anio
## Min. : 8 Length:7000 Min. :2019-07-01 Min. :2019
## 1st Qu.:13006 Class :character 1st Qu.:2019-07-25 1st Qu.:2019
## Median :25648 Mode :character Median :2019-08-16 Median :2019
## Mean :25990 Mean :2019-08-16 Mean :2019
## 3rd Qu.:39084 3rd Qu.:2019-09-08 3rd Qu.:2019
## Max. :52380 Max. :2019-09-29 Max. :2019
## NA's :84 NA's :84
## mes dia medio titulo
## Min. :7.000 Min. : 1.00 Length:7000 Length:7000
## 1st Qu.:7.000 1st Qu.: 8.00 Class :character Class :character
## Median :8.000 Median :15.50 Mode :character Mode :character
## Mean :8.028 Mean :15.66
## 3rd Qu.:9.000 3rd Qu.:24.00
## Max. :9.000 Max. :31.00
## NA's :84 NA's :84
## texto
## Length:7000
## Class :character
## Mode :character
##
##
##
##
Limpieza inicial
En esta primer exploración de las 10 primeras filas del dataset de noticias, podemos identificar algunos artículos que no se han descargado correctamente (por el proceso scrapeador). Como primer medida vamos a quitar estos registros
raw_data_clean <- raw_data %>%
filter(!str_detect(texto, "404 Client Error"))
raw_data_clean## # A tibble: 6,976 × 9
## id url fecha anio mes dia medio titulo texto
## <dbl> <chr> <date> <dbl> <dbl> <dbl> <chr> <chr> <chr>
## 1 51960 https://www.clarin.com… 2019-07-01 2019 7 1 clar… "Copa… "Arg…
## 2 52024 https://www.clarin.com… 2019-07-01 2019 7 1 clar… "Robo… "La …
## 3 52023 https://www.clarin.com… 2019-07-01 2019 7 1 clar… "Inve… "La …
## 4 51863 https://www.perfil.com… 2019-07-01 2019 7 1 perf… "Cier… "mar…
## 5 52087 https://exitoina.perfi… 2019-07-01 2019 7 1 perf… "Tele… "Con…
## 6 52084 https://www.clarin.com… 2019-07-01 2019 7 1 clar… "Vene… "Car…
## 7 51945 https://www.minutouno.… 2019-07-01 2019 7 1 minu… "La a… "El …
## 8 51928 https://www.clarin.com… 2019-07-01 2019 7 1 clar… "¿Mit… "Alg…
## 9 52375 https://www.clarin.com… 2019-07-01 2019 7 1 clar… "Los … "Hay…
## 10 52188 https://www.clarin.com… 2019-07-01 2019 7 1 clar… "Albe… "El …
## # … with 6,966 more rows
De esta manera se redujo el tamaño a 6976 filas.
Además observando el summary de los datos, podemos notar
que la variable fecha contiene varios NA’s. También
filtraremos estos casos.
raw_data_clean <- raw_data_clean %>%
filter(!is.na(fecha))
raw_data_clean## # A tibble: 6,894 × 9
## id url fecha anio mes dia medio titulo texto
## <dbl> <chr> <date> <dbl> <dbl> <dbl> <chr> <chr> <chr>
## 1 51960 https://www.clarin.com… 2019-07-01 2019 7 1 clar… "Copa… "Arg…
## 2 52024 https://www.clarin.com… 2019-07-01 2019 7 1 clar… "Robo… "La …
## 3 52023 https://www.clarin.com… 2019-07-01 2019 7 1 clar… "Inve… "La …
## 4 51863 https://www.perfil.com… 2019-07-01 2019 7 1 perf… "Cier… "mar…
## 5 52087 https://exitoina.perfi… 2019-07-01 2019 7 1 perf… "Tele… "Con…
## 6 52084 https://www.clarin.com… 2019-07-01 2019 7 1 clar… "Vene… "Car…
## 7 51945 https://www.minutouno.… 2019-07-01 2019 7 1 minu… "La a… "El …
## 8 51928 https://www.clarin.com… 2019-07-01 2019 7 1 clar… "¿Mit… "Alg…
## 9 52375 https://www.clarin.com… 2019-07-01 2019 7 1 clar… "Los … "Hay…
## 10 52188 https://www.clarin.com… 2019-07-01 2019 7 1 clar… "Albe… "El …
## # … with 6,884 more rows
summary(raw_data_clean)## id url fecha anio
## Min. : 8 Length:6894 Min. :2019-07-01 Min. :2019
## 1st Qu.:13051 Class :character 1st Qu.:2019-07-25 1st Qu.:2019
## Median :25648 Mode :character Median :2019-08-16 Median :2019
## Mean :25985 Mean :2019-08-16 Mean :2019
## 3rd Qu.:39080 3rd Qu.:2019-09-08 3rd Qu.:2019
## Max. :52375 Max. :2019-09-29 Max. :2019
## mes dia medio titulo
## Min. :7.000 Min. : 1.00 Length:6894 Length:6894
## 1st Qu.:7.000 1st Qu.: 8.00 Class :character Class :character
## Median :8.000 Median :15.50 Mode :character Mode :character
## Mean :8.028 Mean :15.66
## 3rd Qu.:9.000 3rd Qu.:24.00
## Max. :9.000 Max. :31.00
## texto
## Length:6894
## Class :character
## Mode :character
##
##
##
Finalmente nos quedamos con 6894 registros.
Stopwords
A continuación vamos a cargar 2 listados de stopwords o
palabras vacías
El primer listado proviene del libro online Cuentapalabras (Estilometría y análisis de texto con R para filólogos)
stopwords <- read_csv(
"https://raw.githubusercontent.com/7PartidasDigital/AnaText/master/datos/diccionarios/vacias.txt",
locale = default_locale(),
col_names = c("word"))y el segundo listado proviene de un repositorio y sus
contribuciones a las stopwords en varios idiomas.
stopwords_extra <- read.delim(
"https://raw.githubusercontent.com/Alir3z4/stop-words/master/spanish.txt",
col.names = c("word"))y luego unimos ambos dataframes en un único listado de
stopwords
stopwords_final <- bind_rows(stopwords, stopwords_extra) %>%
unique()
# y generamos un vector
stopwords_listado <- stopwords_final$wordEDA (Análisis Exploratorio de los Datos)
Vamos a realizar un análisis inicial para conocer un poquito mejor el dataset de noticias.
Análisis usando bigramas (y gráfos)
Vamos a realizar este análisis con todo el dataset (sin limpieza), esto para conocer de forma “aproximada” las distintas combinaciones de palabras que podemos encontrarnos en las noticias. Esto con la esperanza de detectar ciertos patrones que luego tendremos en cuenta en una segunda instancia de limpieza (quizás al remover stopwords). Por otra parte, puede resultar interesante visualizar la ocurrencia de bigramas en cada medio. Mediante la visualización suelen surgir patrones interesantes y que pueden influir en la toma de decisiones y en la formulación de conclusiones en un análisis.
bigramas <- raw_data %>%
unnest_tokens(bigrama,
texto,
token = "ngrams",
n = 2)
bigramas_separados <- bigramas %>%
separate(bigrama,
c("palabra1", "palabra2"),
sep = " ")
bigramas_filtrados <- bigramas_separados %>%
filter(!palabra1 %in% stopwords_listado,
!palabra2 %in% stopwords_listado)Veamos una visualización “clásica” de los bigramas (top 10 por medio)
top_bigramas_x_medio <- bigramas_filtrados %>%
unite(bigrama, palabra1, palabra2, sep = " ") %>%
count(medio, bigrama, sort = TRUE)
top_bigramas_x_medio %>%
mutate(medio = as.factor(medio)) %>%
group_by(medio) %>%
top_n(10, wt = n) %>%
ggplot(aes(x = n, y = reorder_within(bigrama, n, medio))) +
geom_col(aes(fill = medio), show.legend = FALSE) +
geom_text(aes(label = n), hjust = -.1) +
scale_y_reordered() +
scale_x_continuous(expand = expansion(mult = c(0, .4))) +
labs(y = NULL) +
facet_wrap(vars(medio), scales = "free_y") +
theme_minimal()Ahora vamos a generar una función que genere gráfos
por medio, es decir, a la función le pasamos un medio en particular, por
ejemplo "clarin" y la función realizará el grafo
correspondiente de los bigramas que mayormente ocurren en dicho
medio.
generar_grafo <- function(medio, freq) {
grafo <- bigramas_filtrados %>%
filter(medio == medio) %>%
count(palabra1, palabra2, sort = TRUE) %>%
filter(n > freq) %>%
graph_from_data_frame()
ggraph(grafo, layout = "nicely") +
geom_edge_link(aes(edge_alpha = n),
show.legend = FALSE,
arrow = arrow(type = "closed",
length = unit(3, "mm"))) +
geom_node_point(color = "lightblue", size = 3) +
geom_node_text(aes(label = name), vjust = 1, hjust = 1) +
theme_void()
}Nuestra función además recibe como parámetro el número de frecuencia mínimo de ocurrencia de bigramas.
Generemos el grafo para clarin
# grafo para "clarin"
generar_grafo("clarin", 150)generemos el de pagina12
# grafo para "pagina12"
generar_grafo("pagina12", 150)y finalmente veamos el grafo para minutouno
# grafo para minutouno"
generar_grafo("minutouno", 150)Podemos observar que, con mayor fecuencia, aparecen números en “cadenas” del estilo “10 años”, “20 años”, etc, que denotan tiempo y probablemente no sean de nuestro interés. Además se nombran redes sociales (mail, twitter, facebook, etc) y fechas. Esto lo tendremos en cuenta para más adelante.
Enfoque tradicional (TF-IDF)
El estadístico tf-idf tiene como propósito medir la
importancia de una palabra que ocurre en un documento, en una colección
de documentos (o corpus), en nuestro caso, vamos a ver la “importancia
que cada medio le da a cada palabra”. Notar que podríamos haber
realizado este análisis para cada noticia, pero el objetivo en este caso
será diferenciar por medio y finalmente este análisis contribuirá a
serguir limpiando el dataset para luego hacer
topic modeling.
# tokenizo
data_tokenizado <- raw_data %>%
unnest_tokens(word, texto)
palabras_medio <- data_tokenizado %>%
count(medio, word, sort = TRUE)
palabras_medio <- palabras_medio %>%
bind_tf_idf(word, medio, n)
palabras_medio %>%
group_by(medio) %>%
slice_max(order_by = tf_idf, n = 10) %>%
ggplot(aes(x = tf_idf, y = reorder_within(word, tf_idf, medio))) +
geom_col(aes(fill = medio), show.legend = FALSE) +
#geom_text(aes(label = tf_idf), hjust = -.1) +
# para ordenar las barras al interior de cada facet (se usa en conjunto con "reorder_within")
scale_y_reordered() +
# expandimos un poco a la derecha (40%) para que entre el texto de la barra
scale_x_continuous(expand = expansion(mult = c(0, .4))) +
guides(fill = "none") +
facet_wrap(vars(medio), scales = "free_y") +
labs(x = NULL, y = NULL) +
theme_minimal() +
theme(
axis.text.x = element_blank(),
panel.grid = element_blank()
)Podemos identificar algunas palabras que no quisieramos considerar en
nuestro análisis, como por ejemplo, jpg,
loading, hd, etc. Vamos a considerarlas para
la limpieza definitiva del dataset.
Tokenización + limpieza de stopwords
Ahora vamos a proceder con el proceso de tokenización y luego con la
limpieza de stopwords. Como vimos en el análisis de
bigramas, en nuestros documentos existen números y vamos a removerlos de
nuestro análisis. Además, pudimos identificar del enfoque “tradicional”
tf-idf, algunas palabras que no deberían ser consideradas
en nuestro análisis, por lo que vamos a removerlas.
mystopwords <- tibble(word = c("embed","jpg","loading","hd","protected",
"comentar","guardar","leé","minutouno","páginai","lxs",
"mirá","email","www"))
# tokenizo
data_tokenizado <- raw_data %>%
unnest_tokens(word, texto)
# limpieza definitiva
data_clean <- data_tokenizado %>%
# removemos stopwords
anti_join(stopwords_final) %>%
# "elimino" números
mutate(word = str_extract(word, "[[:alpha:]]+")) %>%
filter(!is.na(word)) %>%
# removemos algunas palabras detectadas "a mano"
anti_join(mystopwords)Un enfoque naive
Veamos rápidamente una forma de obtener el top 10 de palabras más utilizadas por cada medio en un mes en particular (usando el enfoque tf-idf). Para esto vamos a definir una función que nos permita pasarle el mes como argumento.
top_words_medio_mes <- function(mes_filter, n_top = 10){
df <- data_clean %>%
filter(mes == mes_filter) %>%
count(medio, word, sort = TRUE) %>%
bind_tf_idf(word, medio, n) %>%
group_by(medio) %>%
slice_max(order_by = tf_idf, n = n_top)
df %>%
ggplot(aes(x = tf_idf, y = reorder_within(word, tf_idf, medio))) +
geom_col(aes(fill = medio), show.legend = FALSE) +
#geom_text(aes(label = tf_idf), hjust = -.1) +
# para ordenar las barras al interior de cada facet (se usa en conjunto con "reorder_within")
scale_y_reordered() +
# expandimos un poco a la derecha (40%) para que entre el texto de la barra
scale_x_continuous(expand = expansion(mult = c(0, .4))) +
guides(fill = "none") +
facet_wrap(vars(medio), scales = "free_y") +
labs(x = NULL, y = NULL) +
theme_minimal() +
theme(
axis.text.x = element_blank(),
panel.grid = element_blank()
)
}Y ahora llamemos a nuestra función con algunas combinaciones. Veamos por ejemplo para el mes 7
top_words_medio_mes(7)y veamos para el mes 9
top_words_medio_mes(9)No hay mucho para destacar del resultado obtenido si únicamente nos
enfocamos en la métrica de tf-idf por medio. Por esto vamos a realizar
un análisis más avanzado usando topic modeling.
Topic modeling
A continuación vamos a hacer uso de un modelo LDA (del paquete
topicmodels), el cual es un método para ajustar un modelo
de tópicos.
LDA es uno de los algoritmos más cómunes para
topic modeling y se guía por dos principios:
- Todo documento es una mezcla de tópicos
- Todo tópico es una mezcla de palabras
Vamos a dejar de lado la consideración del medio únicamente y ahora cada documento va a estar dentificado por una combinación del medio y el ID de la noticia.
Luego vamos a generar un modelo LDA para 6 tópicos. Por razones de eficiencia y debido a que es un proceso que demora bastante, vamos a guardar el modelo resultante para su posterior carga.
if (file.exists("model_LDA.rda")) {
medios_lda <- readRDS("model_LDA.rda")
} else {
cuenta_palabras <- data_clean %>%
unite(document, medio, id) %>%
count(document, word, sort = TRUE)
medios_tdm <- cuenta_palabras %>%
cast_dtm(document, word, n)
# 6 tópicos
medios_lda <- LDA(medios_tdm, k = 6, control = list(seed = 42))
medios_lda
# guardamos el modelo (pesado!)
saveRDS(medios_lda, "model_LDA.rda")
}Observemos las palabras más “representativas” que el modelo identificó para cada tópico.
medios_temas <- tidy(medios_lda, matrix = "beta")
# top palabras en cada topico o tema
top_temas <- medios_temas %>%
group_by(topic) %>%
slice_max(beta, n = 10) %>%
ungroup() %>%
arrange(topic, -beta)
# Una forma de ver los términos más utilizados en cada tópico
as.data.frame(terms(medios_lda, 10))## Topic 1 Topic 2 Topic 3 Topic 4 Topic 5 Topic 6
## 1 país equipo gobierno años años millones
## 2 años argentina macri policía vida año
## 3 trump partido fernández justicia gusta argentina
## 4 presidente años presidente causa casa dólares
## 5 unidos final frente caso día mercado
## 6 gobierno boca alberto fiscal historia país
## 7 personas copa argentina juicio gente pesos
## 8 mundo fútbol candidato ciudad mujer ciento
## 9 acuerdo river país juez familia años
## 10 mujeres club política seguridad mundo empresas
top_temas %>%
mutate(term = reorder_within(term, beta, topic)) %>%
ggplot(aes(beta, term, fill = factor(topic))) +
geom_col(show.legend = FALSE) +
facet_wrap(~ topic, scales = "free") +
scale_y_reordered()Perfecto! podemos identificar de que cosa trata cada tema o tópic y de esta manera vamos a construir un diccionario de tópicos en donde:
- tópico 1: Internacionales
- tópico 2: Deporte/Fútbol
- tópico 3: Política
- tópico 4: Policial/Judicial
- tópico 5: Sociales/Entretenimiento
- tópico 6: Economía
topicos <- data.frame(
topic = c(1,2,3,4,5,6),
valor = c("Internacionales",
"Deporte/Fútbol",
"Política",
"Policial/Judicial",
"Sociales/Entretenimiento",
"Economía")
)A continuación vamos a generar un dataset que utilize la métrica gamma, que nos indica la probabilidad de un documento de pertenecer a cada tópico o tema.
Además construiremos el dataset de tal manera que podamos identificar los tópicos que predominan en cada medio y en cada fecha.
En primer lugar vamos a obtener el medio y el ID que codificamos en
la variable document.
medios_temas <- tidy(medios_lda, matrix = "gamma")
# veamos la estructura inicial del dataset
head(medios_temas)## # A tibble: 6 × 3
## document topic gamma
## <chr> <int> <dbl>
## 1 clarin_38104 1 0.768
## 2 telam_29686 1 0.0000156
## 3 clarin_4969 1 0.0000443
## 4 lanacion_27049 1 0.0615
## 5 clarin_3614 1 0.0000292
## 6 infobae_45627 1 0.0641
# obtenemos medio e ID de la noticia
medios_temas <- medios_temas %>%
separate(document, into = c("medio", "id"))
# veamos la estructura final del dataset
head(medios_temas)## # A tibble: 6 × 4
## medio id topic gamma
## <chr> <chr> <int> <dbl>
## 1 clarin 38104 1 0.768
## 2 telam 29686 1 0.0000156
## 3 clarin 4969 1 0.0000443
## 4 lanacion 27049 1 0.0615
## 5 clarin 3614 1 0.0000292
## 6 infobae 45627 1 0.0641
Ahora vamos a obtener la fecha de cada noticia, a partir del dataset inicial
medios_temas_fecha <- medios_temas %>%
# para que coincida el tipo de "id" en cada df
mutate(id = as.double(id)) %>%
left_join(raw_data_clean %>% select(id, fecha, mes, dia, texto))
head(medios_temas_fecha)## # A tibble: 6 × 8
## medio id topic gamma fecha mes dia texto
## <chr> <dbl> <int> <dbl> <date> <dbl> <dbl> <chr>
## 1 clarin 38104 1 0.768 2019-07-27 7 27 "En la fábrica de alime…
## 2 telam 29686 1 0.0000156 2019-08-10 8 10 "Las provincias que def…
## 3 clarin 4969 1 0.0000443 2019-09-21 9 21 "La ex presidenta Crist…
## 4 lanacion 27049 1 0.0615 2019-08-14 8 14 "El Harlem Cultural Fes…
## 5 clarin 3614 1 0.0000292 2019-09-24 9 24 "Detalles y servicios e…
## 6 infobae 45627 1 0.0641 2019-07-13 7 13 "Robert James Fischer, …
Genial! ahora tenemos un dataset con el valor de **gamma* para cada noticia por medio y por fecha.
Probemos algunas visualizaciones.
visualización de tópicos “global” por medio
# para cada medio obtenemos los tópicos que suceden con mayor frecuencia
topicos_medio <- medios_temas_fecha %>%
group_by(medio, id) %>%
top_n(1, wt = gamma)
topicos_medio %>%
group_by(medio, topic) %>%
summarise(
total = n()
) %>%
mutate(pct = total / sum(total)) %>%
left_join(topicos) %>%
ggplot(aes(y = reorder_within(valor, pct, medio),
x = pct,
fill = medio)) +
geom_col(show.legend = FALSE) +
labs(y = NULL) +
facet_wrap(~ medio, scales = "free") +
scale_y_reordered()De esta manera podemos observar para cada medio, qué tópico o tema predomina mayormente.
Veamos la misma información pero ahora usando otro tipo de viz (este tipo de visualización me pareció interesante para comparar la variación de la métrica gamma en cada medio).
library(ggradar) # Create radar charts using ggplot2
df_radar <- medios_temas_fecha %>%
group_by(medio, id) %>%
top_n(1, wt = gamma) %>%
group_by(medio, topic) %>%
summarise(
total = n()
) %>%
mutate(pct = total / sum(total)) %>%
left_join(topicos) %>%
select(medio, topic = valor, pct) %>%
pivot_wider(
names_from = topic,
values_from = pct
)
plot_radar_medio <- function(medio_f){
df_radar_filter <- df_radar %>%
filter(medio == medio_f)
ggradar(df_radar_filter)
}Veamos el gráfico para “clarin”
plot_radar_medio("clarin")Podemos observar que existen un mayor (pero no tanto) porcentaje de noticias de “Economía”.
Y veamos ahora el gráfico para “cronica”
plot_radar_medio("cronica")Acá es notable que las noticias del medio “cronica” son mayormente de tema “Policial/Judicial”.
Variación de tópicos por fecha
Veamos cómo varían los tópicos para cada medio por fecha. Para esto vamos a escribir una función que dado un medio, genere una visualización animada.
generar_plot_animado <- function(medio_f) {
anim <- medios_temas_fecha %>%
filter(medio == medio_f) %>%
group_by(id) %>%
top_n(1, wt = gamma) %>%
ungroup() %>%
left_join(topicos) %>%
group_by(fecha, valor) %>%
summarise(
total = n()
) %>%
mutate(Rank = rank(total, ties.method = "first")) %>%
mutate(Rank = as.factor(Rank)) %>%
mutate(pct = total / sum(total)) %>%
mutate(valor = as.factor(valor)) %>%
arrange(fecha, pct) %>%
ggplot() +
geom_col(aes(
x = fct_reorder(Rank, pct),
y = pct,
fill = valor,
group = valor
), width = .7) +
geom_text(aes(
x = fct_reorder(Rank, pct),
y = 0,
label = valor, group = valor),
hjust = 0,
size = 7) +
scale_y_continuous(labels = scales::percent_format()) +
coord_flip(clip = "off") +
labs(y = NULL) +
guides(fill = "none") +
theme_minimal() +
theme(
axis.text.y = element_blank(),
axis.title.y = element_blank(),
axis.text.x = element_text(size = 12),
axis.title.x = element_text(size = 13),
plot.title = element_text(size = 18)
) +
# Animamos la VIZ
ggtitle('Mostrando tópicos para {medio_f} en fecha: {closest_state}') +
transition_states(fecha, state_length = 0, transition_length = 2) +
enter_fly(x_loc = 0, y_loc = 0) +
exit_fly(x_loc = 0, y_loc = 0) +
ease_aes('quadratic-in-out')
animate(anim, width = 700, height = 500,
fps = 25, duration = 60, rewind = FALSE)
}generar_plot_animado("clarin")